package com.google.code.jbauble.mvc;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.code.jbauble.mvc.annotations.RequestMapping;
import com.google.code.jbauble.mvc.support.ActionMapper;
import com.google.code.jbauble.mvc.support.StatusController;
import com.google.code.jbauble.mvc.support.UrlHelper;
import com.google.code.jbauble.mvc.support.WebLoader;
import com.google.code.jbauble.utils.ReflectionUtils;
import com.sun.org.omg.SendingContext.CodeBasePackage.URLHelper;

/**
 * url路由类
 * @author drizzt
 *
 */
public class UrlRouting {
	private final Logger log = LoggerFactory.getLogger(this.getClass());
	private List<ActionMapper> routes = null;
	private List<ActionMapper> regexRoutes = null;
	private DefaultConfiguration config = null;
	
	/**
	 * 构造一个url路由类
	 */
	public UrlRouting(DefaultConfiguration config) {
		routes = new ArrayList();
		regexRoutes = new ArrayList();
		this.config = config;
	}
	
	/**
	 * 添加一个controller到多个路由
	 * @param controller 控制器
	 */
	public void addRoutes(Object controller) {
		if (controller == null)
			return;
		
		Class c = controller.getClass();
		String controllerUrl = getControllerUrl(c);
		
		Method[] ms = c.getMethods();
		if (ms == null || ms.length < 1)
			return;
		
		for (Method m : ms) {
			int modifier = m.getModifiers();
			if (!Modifier.isPublic(modifier) || !m.getReturnType().equals(Result.class))
				continue;
			
			addRoute(controllerUrl, controller, m);
		}
	}
	
	public void addRoutes(Class clazz) {
		if (clazz == null)
			return;
		
		Object controller = null;
		try {
			controller = clazz.newInstance();
		} catch (InstantiationException e) {
			log.error("Add routes error.", e);
		} catch (IllegalAccessException e) {
			log.error("Add routes error.", e);
		}
		addRoutes(controller);
	}

	/**
	 * 从控制类型得到名称
	 * @param c 控制类
	 * @return 控制类url名称
	 */
	private String getControllerUrl(Class c) {
		String controllerUrl = null;
		Annotation a = c.getAnnotation(RequestMapping.class);
		RequestMapping ca = (RequestMapping)a;
		if (ca != null) {
			controllerUrl = ca.path();
		}
		else {
			String name = c.getSimpleName();
			int index = name.indexOf("Controller");
			if (index > -1) {
				name = name.substring(0, index);
			}
		
			controllerUrl = name.substring(0, 1).toLowerCase() + name.substring(1);
		}
		return controllerUrl;
	}

	public void addRoute(String controllerUrl, Object controller, String methodUrl, Method method, int httpMethod) {
		addRoute(controllerUrl, controller, false, methodUrl, method, httpMethod);
	}
	
	public void addRoute(String controllerUrl, Object controller, boolean isRegex, String methodUrl, Method method, int httpMethod) {
		ActionMapper mapper = new ActionMapper(controllerUrl, controller, isRegex, methodUrl, method, httpMethod, config);
		if (isRegex) {
			regexRoutes.add(mapper);
		}
		else {
			routes.add(mapper);
		}
	}
		
	/**
	 * 添加一个路由
	 * @param controllerUrl 控制器url
	 * @param controller 控制器
	 * @param method 方法
	 */
	public void addRoute(String controllerUrl, Object controller, Method method) {
		String methodUrl = null;
		int httpMethod = UrlHelper.HTTP_ALL;
		RequestMapping a = method.getAnnotation(RequestMapping.class);
		if (a != null) {
			methodUrl = a.path();
			httpMethod = a.method();
		}
		if (methodUrl == null || methodUrl.equals("")){
			methodUrl = method.getName();
			if (methodUrl.equals("index")) {
				methodUrl = "";
			}
		}
		
		addRoute(controllerUrl, controller, isRegex(methodUrl), methodUrl, method, httpMethod);
	}
	
	private static Pattern regexPattern = Pattern.compile("[^\\w\\d/]+", Pattern.CASE_INSENSITIVE);
	
	private boolean isRegex(String methodUrl) {
		Matcher matcher = regexPattern.matcher(methodUrl);
		return matcher.find();
	}

	public void addRoute(Object controller, Method method) {
		Class c = controller.getClass();
		String controllerUrl = getControllerUrl(c);
		addRoute(controllerUrl, controller, method);
	}
	
	public void addRoute(Object controller, String methodName) {
		Class c = controller.getClass();
		String controllerUrl = getControllerUrl(c);
		
		Method[] ms = c.getMethods();
		if (ms == null || ms.length < 1)
			return;
		
		for (Method m : ms) {
			int modifier = m.getModifiers();
			if (!Modifier.isPublic(modifier) || !m.getName().equals(methodName))
				continue;
			
			addRoute(controllerUrl, controller, m);
			break;
		}
	}

	public ActionMapper getActionMapper(HttpServletRequest request) {
		if (routes == null || routes.size() < 1)
			return null;
		String url = UrlHelper.getRequestUrl(request);
		for (ActionMapper m : routes) {
			if (m.isMatchUrl(UrlHelper.getHttpMehtod(request.getMethod()), url))
				return m;
		}
		for (ActionMapper m : regexRoutes) {
			if (m.isMatchRegexUrl(UrlHelper.getHttpMehtod(request.getMethod()), url))
				return m;
		}
		return null;
	}

	public void addRoutesFromPackage(String pack) {
		Package p = Package.getPackage(pack);
		if (p == null) {
			String path = WebLoader.getWebClassesPath(System.getProperty(DefaultConfiguration.WEB_CONF_PATH));
			String[] clzz = ReflectionUtils.getClassesFromPackage(path, pack);
			if (clzz == null ||clzz.length < 1)
				return;
			
			for (String c : clzz) {
				try {
					addRoutes(Class.forName(WebLoader.getFullClassName(pack, c)));
				} catch (ClassNotFoundException e) {
					log.error("Add routes from package error." + pack, e);
				}
			}
		}
		else {
			Set<Class<?>> cs = ReflectionUtils.getClasses(Package.getPackage(pack));
			if (cs == null ||cs.size() < 1)
				return;
			
			for (Class c : cs) {
				addRoutes(c);
			}
		}
	}

	public void addRoute(String url, Class clazz, String method, int httpMethod) {
		Object obj = null;
		try {
			obj = clazz.newInstance();
		} catch (InstantiationException e) {
			log.error("Add route error." + url, e);
			return;
		} catch (IllegalAccessException e) {
			log.error("Add route error." + url, e);
			return;
		}
		
		addRoute(url, obj, method, httpMethod);
	}
	
	public void addRoute(String url, Object controller, String method, int httpMethod) {
		Method m = findMethod(controller, method);
		if (m == null)
			return;
		ActionMapper mapper = new ActionMapper(url, controller, false, "", m, httpMethod, config);
		routes.add(mapper);
	}
	
	private Method findMethod(Object controller, String method) {
		Class c = controller.getClass();
		Method[] ms = c.getMethods();
		if (ms == null || ms.length < 1)
			return null;
		
		for (Method m : ms) {
			int modifier = m.getModifiers();
			if (!Modifier.isPublic(modifier) || !m.getName().equals(method))
				continue;
			
			return m;
		}
		return null;
	}
}
